#include <iostream>
#include "Universal/Universal.h"
#include KRTS_HEAD

void OnCloseDriver()
{
    if (const KSError error = KS_closeDriver(); error != KS_OK)
    {
        Universal::OutputErr(error, "KS_closeDriver", "Unable to close the driver!");
    }

    Universal::WaitTime(500 * ms_);

    printf("Press any key to exit!\n");
    getchar();
}

int main(int argc, char *argv[])
{
    // 显示欢迎信息
    printf("\n");
    printf("******************************************************************** \n");
    printf("*                                                                  * \n");
    printf("*      Welcome to the EzCode KRTS 'Jitter Measurement Tool'        * \n");
    printf("*                                                                  * \n");
    printf("******************************************************************** \n");
    printf("\n");

    // 打开设备，KRTS初始化必须
    KSError error = KS_openDriver(CUSTOM_NUMBER);                        // 客户编号
    if (error != KS_OK)
    {
        Universal::OutputErr(error, "KS_openDriver", "Unable to open the driver!");
        printf("Press any key to exit!\n");
        getchar();
        return -1;
    }

    // 创建共享内存
    KSHandle shared_memory;
    error = KS_createSharedMemEx(&shared_memory, "", sizeof(SharedData), KSF_NO_FLAGS);
    if (error != KS_OK)
    {
        Universal::OutputErr(error, "KS_createSharedMemEx", "Unable to create shared memory!");
        OnCloseDriver();
        return -1;
    }

    // 映射共享内存
    SharedData *shared_data {};
    error = KS_getSharedMemEx(shared_memory, reinterpret_cast<void **>(&shared_data), KSF_NO_FLAGS);
    if (error != KS_OK)
    {
        Universal::OutputErr(error, "KS_getSharedMemEx", "Unable to map shared memory!");
        OnCloseDriver();
        return -1;
    }

    // 获取系统信息
    KSSystemInformation system_info;
    system_info.structSize = sizeof(KSSystemInformation);
    error = KS_getSystemInformation(&system_info, KSF_NO_FLAGS);
    if (error != KS_OK)
    {
        Universal::OutputErr(error, "KS_getSystemInformation", "Unable to get system information to distinguish bit size!");
        OnCloseDriver();
        return -1;
    }

    // 加载内核
    error = KS_loadKernel(&shared_data->kernel_handle, "Kernel.dll", nullptr, nullptr, KSF_KERNEL_EXEC);
    if (error != KS_OK)
    {
        Universal::OutputErr(error, "KS_loadKernel", "Unable to load DLL! Is the DLL in the search path?");
        OnCloseDriver();
        return -1;
    }

    // 显示可用的CPU数量
    const int dedicated_cp_us_avail = system_info.numberOfCPUs - system_info.numberOfSharedCPUs;
    // 显示CPU信息
    printf(" ===================================================================  \n");
    printf("|                  System Configuration Information                 | \n");
    printf("|===================================================================| \n");
    printf("|Total number of CPUs|Number of shared CPUs|Number of exclusive CPUs| \n");
    printf("|         %2d         |          %2d         |          %2d            | \n", system_info.numberOfCPUs, system_info.numberOfSharedCPUs, dedicated_cp_us_avail);
    printf(" ===================================================================  \n");
    printf("\n");
    printf(" ===================================================================  \n");
    printf("|                    Apply Option Configurations                    | \n");
    printf(" ===================================================================  \n");
    printf("\n");
#ifdef KS_TASK_MODULE_INCLUDED
    // Windows 使用的 CPU 数量也是第一个专用 CPU 的索引。
    const int fall_back_cpu = system_info.numberOfSharedCPUs;
    int cpu = Universal::InputDec("- Set the exclusive CPU:", fall_back_cpu);
    if (cpu < system_info.numberOfSharedCPUs || cpu >= system_info.numberOfCPUs)
    {
        Universal::OutputDec(cpu, "Sorry, CPU ", " is not available. ", false);
        Universal::OutputDec(fall_back_cpu, "Using CPU ", " instead.");
        cpu = fall_back_cpu;
    }

    // 设置目标处理器,内核代码执行在这个CPU上
    error = KS_setTargetProcessor(cpu,KSF_NO_FLAGS);
    if (error != KS_OK)
    {
        Universal::OutputErr(error, "KS_setTargetProcessor", "Failed to set target processor!");
        OnCloseDriver();
        return -1;
    }
#endif  // KS_TASK_MODULE_INCLUDED

    // 设置测量的时间单位
    const int unit = Universal::InputDec("- Set the measuring unit [0]second [1]minute [2]hour:", 1);
    int time_unit = 1;
    int duration = 0;
    switch (unit)
    {
        case 0:
        {
            time_unit = 1;
            duration = Universal::InputDec("- Set the test time (unit:seconds):", 10);
            break;
        }
        case 1:
        {
            time_unit = 60;
            duration = Universal::InputDec("- Set the test time (unit:minutes):", 10);
            break;
        }
        case 2:
        {
            time_unit = 60 * 60;
            duration = Universal::InputDec("- Set the test time (unit:hours):", 10);
            break;
        }
        default:
            break;
    }

    // 测量时间
    if (duration < 1)
    {
        printf("Sorry, the measuring duration will be limited to 1 !");
        duration = 1;
    }

    // 1毫秒的周期位单位计数
    shared_data->items = duration * 1000 * time_unit;              // 定时器将以 1 kHz 的频率运行 1us
    shared_data->pre_measure_count = 10;                           // 开始测量前的计时器滴答声数
    shared_data->index = 0;                                        // 清除用于电流测量的索引计数器

    // 初始化内核代码
    error = KS_execKernelFunctionEx(shared_data->kernel_handle, "InitFunction", shared_memory, KS_INVALID_HANDLE, KSF_NO_FLAGS);
    if (error != KS_OK)
    {
        Universal::OutputErr(error, "KS_execKernelFunctionEx", "Unable to initialize the Kernel DLL!");
        OnCloseDriver();
        return -1;
    }

    // 启动定时器
    error = KS_startTimer(shared_data->timer_handle, KSF_NO_FLAGS, 0);
    if (error != KS_OK)
    {
        Universal::OutputErr(error, "KS_startTimer", "Unable to start the timer!");
        OnCloseDriver();
        return -1;
    }

    // 显示测量结果
    printf(" ===================================================================  \n");
    printf("|            Operating Mode: Kernel Mode, Display unit: us          | \n");
    printf("|            Press 'Q' to exit                                      | \n");
    printf(" ===================================================================  \n");
    printf("\n");
    int show_tail {0};
    while (!shared_data->finish_flag)
    {
        if (show_tail % 20 == 0)
        {
            printf("| lat min | lat avg | lat max | all min | all max |current index| \n");
        }
        printf("|%8.2lf |%8.2lf |%8.2lf |%8.2lf |%8.2lf |%12lld | \n",
            static_cast<double> (shared_data->measure_value.lat_min) / 10.0, static_cast<double> (shared_data->measure_value.lat_avg) / 10.0,
            static_cast<double> (shared_data->measure_value.lat_max) / 10.0, static_cast<double> (shared_data->measure_value.min_value) / 10.0,
            static_cast<double> (shared_data->measure_value.max_value) / 10.0, shared_data->measure_value.current_time);

        show_tail++;
        Universal::WaitTime(1000 * ms_);

        if (Universal::EzKbhit())
        {
            const int key = Universal::EzGetch();
            if (key == 'q' || key == 'Q')
            {
                shared_data->finish_flag = 1;
            }
        }
    }

    printf("\n");
    printf(" ===================================================================  \n");
    printf("|             Jitter Measurement Statistical Reports                | \n");
    printf(" ===================================================================  \n");
    printf("\n");

    for (int i = 0; i < 10; ++i)
    {
        char output_row[256];
        sprintf(output_row, "%3d.0 ... %3d.9 us  count:  %12d Percentage: %02.2f%s \n", i, i, shared_data->classes[i], shared_data->classes[i] * 100.0 / static_cast<double> (shared_data->measure_value.current_time), "%%");
        printf(output_row);
    }

    printf("\n");
    Universal::OutputFloat(static_cast<double> (shared_data->measure_value.min_value) / 10.0, "  Minimum jitter: ", " us");
    Universal::OutputFloat(static_cast<double> (shared_data->measure_value.max_value) / 10.0, "  Maximum jitter: ", " us");
    Universal::OutputFloat(static_cast<double> (shared_data->measure_value.max_value - shared_data->measure_value.min_value) / 10.0, "  jitter Value: ", " us");
    printf("\n");

    // 释放内核代码资源
    error = KS_execKernelFunctionEx(shared_data->kernel_handle, "ExitFunction", KS_INVALID_HANDLE, KS_INVALID_HANDLE, KSF_NO_FLAGS);
    if (error != KS_OK)
    {
        Universal::OutputErr(error, "KS_execKernelFunctionEx", "Error while deallocating resources on Kernel level!");
        OnCloseDriver();
        return -1;
    }

    //------------------------------------------------------------------------------------------------------------
    // 使用共享句柄卸载内核 DLL。
    // 尽管 KS_closeDriver（） 释放了所有分配的资源（如共享内存和加载的内核），但显式提前释放您分配的资源是一种很好的方式。
    //------------------------------------------------------------------------------------------------------------
    error = KS_freeKernel(shared_data->kernel_handle);
    if (error != KS_OK) {
        Universal::OutputErr(error, "KS_freeKernel", "Unable to unload the Kernel!");
    }

    //------------------------------------------------------------------------------------------------------------
    //使用其本地存储句柄释放共享内存。
    //------------------------------------------------------------------------------------------------------------
    error = KS_freeSharedMemEx(shared_memory, KSF_NO_FLAGS);
    if (error != KS_OK) {
        Universal::OutputErr(error, "KS_freeSharedMemEx", "Unable to remove shared memory!");
    }

    //------------------------------------------------------------------------------------------------------------
    //最后，我们必须关闭驱动程序以释放任何分配的资源。
    //------------------------------------------------------------------------------------------------------------
    OnCloseDriver();
    return 0;
}
